package com.ruihu.admin.module.business.tasknode.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Pair;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruihu.admin.common.constant.ResponseCodeConst;
import com.ruihu.admin.common.domain.PageResultDTO;
import com.ruihu.admin.common.domain.ResponseDTO;
import com.ruihu.admin.module.business.node.config.constant.TaskProcessConfigResponseCodeConst;
import com.ruihu.admin.module.business.task.dao.SubtaskDefectDao;
import com.ruihu.admin.module.business.tasknode.dao.NodeInstProcessDefectDao;
import com.ruihu.admin.module.business.tasknode.dao.NodeInstProcessRelationDao;
import com.ruihu.admin.module.business.tasknode.dao.TaskNodeFileDao;
import com.ruihu.admin.module.business.tasknode.domain.dto.*;
import com.ruihu.admin.module.business.tasknode.domain.entity.NodeInstProcessDefectEntity;
import com.ruihu.admin.module.business.tasknode.domain.entity.NodeInstProcessRelationEntity;
import com.ruihu.admin.module.business.tasknode.domain.entity.SubTaskNodeInstEntity;
import com.ruihu.admin.module.business.tasknode.domain.vo.NodeInstProcessRelationVO;
import com.ruihu.admin.util.BeanUtil;
import com.ruihu.admin.util.PageUtil;
import com.ruihu.admin.util.RequestTokenUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * [  ]
 *
 * @author Tony
 * @version 1.0
 * @company 瑞虎（南京）信息技术有限公司
 * @copyright (c)  瑞虎（南京）信息技术有限公司Inc. All rights reserved.
 * @date 2023-01-03 14:55:43
 * @since JDK1.8
 */
@Service
public class NodeInstProcessRelationService {

    @Autowired
    private NodeInstProcessRelationDao nodeInstProcessRelationDao;

    @Autowired
    private NodeInstProcessDefectDao nodeInstProcessDefectDao;

    @Autowired
    private TaskNodeFileDao taskNodeFileDao;

    @Autowired
    private SubtaskDefectDao subtaskDefectDao;

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    /**
     * 根据id查询
     */
    public NodeInstProcessRelationEntity getById(Long id){
        return  nodeInstProcessRelationDao.selectById(id);
    }

    /**
     * 分页查询
     * @author Tony
     * @date 2023-01-03 14:55:43
     */
    public ResponseDTO<PageResultDTO<NodeInstProcessRelationVO>> queryByPage(NodeInstProcessRelationQueryDTO queryDTO) {
        Page page = PageUtil.convert2QueryPage(queryDTO);
        List<NodeInstProcessRelationVO> voList = nodeInstProcessRelationDao.queryByPage(page, queryDTO);
        page.setRecords(voList);
        PageResultDTO<NodeInstProcessRelationVO> pageResultDTO = PageUtil.convert2PageResult(page);
        return ResponseDTO.succData(pageResultDTO);
    }

    /**
     * 添加
     * @author Tony
     * @date 2023-01-03 14:55:43
     */
    public ResponseDTO<String> add(NodeInstProcessRelationAddDTO addDTO) {
        NodeInstProcessRelationEntity entity = BeanUtil.copy(addDTO, NodeInstProcessRelationEntity.class);
        nodeInstProcessRelationDao.insert(entity);
        return ResponseDTO.succ();
    }

    /**
     * 编辑
     * @author Tony
     * @date 2023-01-03 14:55:43
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> update(NodeInstProcessRelationUpdateDTO updateDTO) {
        NodeInstProcessRelationEntity entity = BeanUtil.copy(updateDTO, NodeInstProcessRelationEntity.class);
        nodeInstProcessRelationDao.updateById(entity);
        return ResponseDTO.succ();
    }

    /**
     * 批量删除
     * @author Tony
     * @date 2023-01-03 14:55:43
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> deleteByIds(List<Long> idList) {
        nodeInstProcessRelationDao.deleteByIdList(idList);
        return ResponseDTO.succ();
    }

    /**
     * 根据ID删除
     * @author Tony
     * @date 2023-01-03 14:55:43
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> deleteById(Long id) {
        NodeInstProcessRelationEntity nodeInstProcessRelationEntity = this.nodeInstProcessRelationDao.selectById(id);
        if (Objects.nonNull(nodeInstProcessRelationEntity)) {
            // 删除元素的附件
            this.taskNodeFileDao.deleteByNodeProcessRelationId(id);
            // 删除元素
            this.nodeInstProcessRelationDao.deleteById(id);

            return ResponseDTO.succ();
        }

        return ResponseDTO.wrap(ResponseCodeConst.NOT_EXISTS);
    }

    /**
     * 批量更新
     * @param relations
     * @return
     */
    public ResponseDTO<String> batchUpdateByIds(List<NodeInstProcessRelationUpdateDTO> relations) {
        if(CollectionUtils.isEmpty(relations)){
            return ResponseDTO.wrap(TaskProcessConfigResponseCodeConst.NO_DATA);
        }
        List<NodeInstProcessRelationEntity> entities = new ArrayList<>();
        relations.forEach(nodeInstProcessRelationUpdateDTO -> {
            NodeInstProcessRelationEntity entity = BeanUtil.copy(nodeInstProcessRelationUpdateDTO, NodeInstProcessRelationEntity.class);
            entity.setUpdateTime(new Date());
            entities.add(entity);
        });
        nodeInstProcessRelationDao.batchUpdateByIds(entities);
        return ResponseDTO.succ();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> addNodeInstProcessRelation(SubTaskNodeInstEntity subTaskNodeInstEntity, List<NodeInstProcessRelationUpdateDTO> nodeInstProcessRelationUpdateList) {
        Assert.notNull(subTaskNodeInstEntity);
        if (CollectionUtil.isEmpty(nodeInstProcessRelationUpdateList))
            return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM);

        SqlSession sqlSession = this.sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
        List<Pair<NodeInstProcessRelationEntity, NodeInstProcessRelationUpdateDTO>> pairList = new ArrayList<>();
        nodeInstProcessRelationUpdateList.stream().forEach(nodeInstProcessRelationUpdateDTO -> {
            NodeInstProcessRelationEntity nodeInstProcessRelationEntity = NodeInstProcessRelationEntity.convertToNodeInstProcessRelationEntity(nodeInstProcessRelationUpdateDTO);
            nodeInstProcessRelationEntity.setTaskNodeInstId(subTaskNodeInstEntity.getTaskNodeInstId());
            nodeInstProcessRelationEntity.setSubTaskNodeInstId(subTaskNodeInstEntity.getId());
            nodeInstProcessRelationEntity.setCreateTime(new Date());
            this.nodeInstProcessRelationDao.insert(nodeInstProcessRelationEntity);
            pairList.add(Pair.of(nodeInstProcessRelationEntity, nodeInstProcessRelationUpdateDTO));

            // 保存缺陷类型
            if (CollectionUtil.isNotEmpty(nodeInstProcessRelationUpdateDTO.getDefectTypeList())) {
                List<DefectTypeDTO> defectTypeDTOList = nodeInstProcessRelationUpdateDTO.getDefectTypeList();
                defectTypeDTOList.forEach(defectTypeDTO -> {
                    NodeInstProcessDefectEntity nodeInstProcessDefectEntity = new NodeInstProcessDefectEntity();
                    nodeInstProcessDefectEntity.setSubTaskId(subTaskNodeInstEntity.getSubTaskId());
                    nodeInstProcessDefectEntity.setTaskNodeInstId(subTaskNodeInstEntity.getTaskNodeInstId());
                    nodeInstProcessDefectEntity.setProcessRelationId(nodeInstProcessRelationEntity.getId());
                    nodeInstProcessDefectEntity.setDefectTypeId(defectTypeDTO.getDefectTypeId());
                    nodeInstProcessDefectEntity.setDefectName(defectTypeDTO.getDefectName());
                    nodeInstProcessDefectEntity.setCreateTime(new Date());
                    nodeInstProcessDefectEntity.setCreateBy(RequestTokenUtil.getThreadLocalUser().getRequestUserId());
                    this.nodeInstProcessDefectDao.insert(nodeInstProcessDefectEntity);
                });
            }
        });
        sqlSession.commit();

        // 设置数据库中保存的ID
        pairList.forEach(e -> e.getValue().setId(e.getKey().getId()));

        return ResponseDTO.succ();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> updateNodeInstProcessRelation(SubTaskNodeInstEntity subTaskNodeInstEntity, List<NodeInstProcessRelationUpdateDTO> updateNodeProcessRelationList) {
        Assert.notNull(subTaskNodeInstEntity);
        if (CollectionUtil.isEmpty(updateNodeProcessRelationList))
            return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM);

        List<Long> idList = updateNodeProcessRelationList.stream().map(NodeInstProcessRelationUpdateDTO::getId).collect(Collectors.toList());
        // 查出所有已存在的元素
        List<NodeInstProcessRelationEntity> existsNodeInstProcessRelationEntityList = this.nodeInstProcessRelationDao.selectBatchIds(idList);
        // 查找已经存在的缺陷
        NodeInstProcessDefectQueryDTO defectQueryDTO = new NodeInstProcessDefectQueryDTO();
        defectQueryDTO.setProcessRelationIdList(existsNodeInstProcessRelationEntityList.stream()
                .map(NodeInstProcessRelationEntity::getId)
                .collect(Collectors.toList()));
        Map<String, Map<String, NodeInstProcessDefectDTO>> nodeInstProcessDefectMap = this.nodeInstProcessDefectDao.queryByList(defectQueryDTO)
                .stream()
                .collect(Collectors.groupingBy(e -> e.getProcessRelationId().toString(),
                        Collectors.toMap(f -> f.getDefectTypeId().toString(), f -> f)));
        SqlSession sqlSession = this.sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
        updateNodeProcessRelationList.stream().forEach(e -> {
            // 查寻取消选择的缺陷类型
            Optional<NodeInstProcessRelationEntity> optional = existsNodeInstProcessRelationEntityList.stream().filter(x -> x.getId().equals(e.getId())).findFirst();
            if (optional.isPresent()) {
                NodeInstProcessRelationEntity existsEntity = optional.get();

                // 保存缺陷关联
                Map<String, NodeInstProcessDefectDTO> existsDefectMap = nodeInstProcessDefectMap.get(existsEntity.getId().toString());
                if (Objects.isNull(existsDefectMap))
                    existsDefectMap = new HashMap<>();

                List<DefectTypeDTO> defectTypeDTOList = e.getDefectTypeList() == null ? new ArrayList<>() : e.getDefectTypeList();
                // 新增缺陷类型
                if (CollectionUtil.isNotEmpty(defectTypeDTOList)) {
                    for (DefectTypeDTO defectTypeDTO : defectTypeDTOList) {
                        if (!existsDefectMap.containsKey(defectTypeDTO.getDefectTypeId().toString())) {
                            NodeInstProcessDefectEntity defectEntity = new NodeInstProcessDefectEntity();
                            defectEntity.setSubTaskId(subTaskNodeInstEntity.getSubTaskId());
                            defectEntity.setTaskNodeInstId(subTaskNodeInstEntity.getTaskNodeInstId());
                            defectEntity.setProcessRelationId(existsEntity.getId());
                            defectEntity.setDefectTypeId(defectTypeDTO.getDefectTypeId());
                            defectEntity.setDefectName(defectTypeDTO.getDefectName());
                            defectEntity.setCreateBy(RequestTokenUtil.getThreadLocalUser().getRequestUserId());
                            defectEntity.setCreateTime(new Date());
                            this.nodeInstProcessDefectDao.insert(defectEntity);
                        }
                    }
                }

                // 删除需要删除的缺陷列表
                Set<String> defectTypeIdSet = defectTypeDTOList.stream().map(g -> g.getDefectTypeId().toString()).collect(Collectors.toSet());
                List<Long> removeDefectTypeRelationIdList = new ArrayList<>();
                for (Map.Entry<String, NodeInstProcessDefectDTO> entry : existsDefectMap.entrySet()) {
                    if (!defectTypeIdSet.contains(entry.getKey())) {
                        removeDefectTypeRelationIdList.add(entry.getValue().getId());
                    }
                }
                if (removeDefectTypeRelationIdList.size() > 0)
                    this.nodeInstProcessDefectDao.deleteBatchIds(removeDefectTypeRelationIdList);

                NodeInstProcessRelationEntity nodeInstProcessRelationEntity = NodeInstProcessRelationEntity.convertToNodeInstProcessRelationEntity(e);
                nodeInstProcessRelationEntity.setUpdateTime(new Date());
                nodeInstProcessRelationEntity.setSubTaskNodeInstId(subTaskNodeInstEntity.getId());
                this.nodeInstProcessRelationDao.updateById(nodeInstProcessRelationEntity);
            }
        });
        sqlSession.commit();

        return ResponseDTO.succ();
    }
}
